<?php
//---------------------------------------------------------------------

/**
 * @file			apiBase.php
 * @CopyRight		(C)1996-2099 SINA Inc.
 * @Project			Waitan
 * @Author			xionghui <xionghui1@staff.sina.com.cn>
 * @Create Date:	2011-08-12
 * @Modified By:	xionghui/2011-08-12
 * @Brief			API核心文件
 */
//---------------------------------------------------------------------

/// 脚本关闭时的处理函数
register_shutdown_function(array(
	'vApi',
	'shutDown'
));
/**
  *  引入一个插件引用 
  */
function require_plugins($filename) {
	require_once (API_PLUGINS_PATH . '/' . $filename);
}
/// API 核心类

class vApi {
	static $curRoute;
	static $curAuth;
	
	function vApi() {
	}
	//-----------------------------------------------------------------
	
	/// 调用一个 api $isInterCall 是否API调用API内部调用,采用继承式授权方式
	
	function api($apiRoute, $p, $isInterCall = FALSE) {
		static $apiFileList = array();
		//清空 最后一次错误，解决最后一次
		vApi::kv('error#last_err', NULL);
		self::isInterCall($isInterCall);
		if ($isInterCall) {
			try {
				$rArr = vApi::parseApiRoute($apiRoute);
			}
			catch(Exception $e) {
			}
			$apiRoute.= empty($rArr['auth']) ? '.' . vApi::kv('API-LAST-AUTH') : '';
		}
		/// debug view
		vApi::log($apiRoute);
		vApi::kv('API-LAST-CALL', $apiRoute);
		vApi::kv('API-PARAMS-DEBUG', isset($p['debug']));
		/// 异常捕获
		try {
			$rArr = vApi::parseApiRoute($apiRoute);
			$route = $rArr['route'];
			$auth = $rArr['auth'];
			$apiFunc = $rArr['apiFunc'];
			$apiFile = $rArr['apiFile'];
			self::authMethod($auth);
			vApi::log('AUTH-NAME', $auth);
			vApi::kv('API-LAST-AUTH', $auth);
			vApi::auth($auth);
			vApi::log('USER-UID', vApi::uid());
			if (!isset($apiFileList[$route])) {
				if (!file_exists($apiFile)) {
					vApi::throwException('110002', '无法找到API文件[' . $apiFile . ']');
				}
				require_once $apiFile;
				$apiFileList[$route] = $apiFile;
				self::$curRoute = $route;
			}
			if (!function_exists($apiFunc)) {
				vApi::throwException('110003', '无法在API文件[' . $apiFile . ']中找到API方法[' . $apiFunc . ']');
			}
			vApi::log('API-PARAMS', $p);
			$rst = $apiFunc($p);
			vApi::log('API-RST', $rst);
			// 输出格式化
			$_rFormat = isset($p['_format']) ? $p['_format'] : self::returnFormat();
			if ($_rFormat) {
				$rst = vApi::getClass('apiFormat')->init($_rFormat)->doFormat($rst);
			}
			//根据_iter参数决定是否将返回的数据去非字串key
			$_rIter = isset($p['_iter']) ? (boolean)$p['_iter'] : FALSE;
			if ($_rIter) {
				$rst = vApi::getBase('format')->iterRst($rst);
			}
			$inRst['rst'] = $rst;
			return vApi::rst($inRst);
		}
		catch(Exception $e) {
			if (self::isInterCall(TRUE, TRUE)) {
				throw $e;
			} // API调用API,继续抛出异常
			if (method_exists($e, 'apiErr')) {
				return $e->apiErr();
			}
			throw $e;
		}
	}
	//-----------------------------------------------------------------
	
	/// 通过HTTP 调用API请求
	
	function doApiRequest() {
		if (strtolower(PHP_SAPI) == "cli") {
			global $argv;
			if (!isset($argv[1])) {
				vApi::throwException('110012', 'CLI调用时，参数不够，最少提供API参数');
			}
			return vApi::api($argv[1], vApi::parseCliParams());
		}
		return vApi::api(vApi::getHttpApiRoute() , vApi::getHttpApiParams());
	}
	//解释命令行的参数
	
	function parseCliParams() {
		global $argv;
		if (count($argv) < 3) {
			return TRUE;
		}
		for ($i = 2;$i < count($argv);$i++) {
			$tmp = explode(":", $argv[$i], 2);
			$tmp[0] = strtoupper($tmp[0]);
			if ($tmp[0] == 'POST') {
				$tmpArr = array();
				parse_str($tmp[1], $tmpArr);
				$_POST = array_merge($_POST, $tmpArr);
			}
			if ($tmp[0] == 'GET') {
				$tmpArr = array();
				parse_str($tmp[1], $tmpArr);
				$_GET = array_merge($_GET, $tmpArr);
			}
			if ($tmp[0] == 'COOKIE') {
				$tmpArr = array();
				parse_str($tmp[1], $tmpArr);
				$_COOKIE = array_merge($_COOKIE, $tmpArr);
			}
		}
		$_REQUEST = array_merge($_POST, $_GET, $_COOKIE);
		return $_REQUEST;
	}
	//-----------------------------------------------------------------
	
	/**
	 * 获取一个变量配置，配置信息存储在全局变量　$G_API_CONFIG;　中
	 * @param mixed $k1
	 * @param mixed $k2
	 * @return 返回相应的配置信息
	 */
	
	function cfg($k1, $k2 = FALSE) {
		global $G_API_CONFIG;
		return $k2 ? $G_API_CONFIG[$k1][$k2] : $G_API_CONFIG[$k1];
	}
	//-----------------------------------------------------------------
	
	/// 当前访问API的用户UID
	
	function uid($uid = NULL) {
		static $stUid = 0;
		if (func_num_args()) {
			$stUid = $uid;
		}
		return $stUid;
	}
	//-----------------------------------------------------------------
	
	/// 当前访问API的管理员UID
	
	function aid($aid = NULL) {
		static $stAid = 0;
		if (func_num_args()) {
			$stAid = $aid;
		}
		return $stAid;
	}
	//-----------------------------------------------------------------
	
	/// 当前访问API的认证方法
	
	function authMethod($method = NULL) {
		static $stAuthMethod = 0;
		if (func_num_args()) {
			$stAuthMethod = $method;
		}
		return $stAuthMethod;
	}
	//-----------------------------------------------------------------
	
	/// md5　别名
	
	function vMd5($str, $rand = '') {
		return md5($str . '#' . API_MD5_SOLT_STR . '#' . $rand);
	}
	//-----------------------------------------------------------------
	
	//执行权限检查
	
	function auth($auth) {
		static $authObj = array();
		$authData = explode('-', $auth);
		$authName = $authData[0];
		$authExt = isset($authData[1]) ? $authData[1] : '';
		if (!isset($authObj[$authName])) {
			$clsName = $authName . API_AUTH_CLS_SUFFIX;
			if (!class_exists($clsName)) {
				$clsFile = API_AUTH_PATH . '/' . $authName . API_AUTH_FILE_SUFFIX;
				if (!file_exists($clsFile)) {
					vApi::throwException('110010', '未知的授权方式', '无法找到auth类文件[' . $clsFile . ']');
				}
				require_once $clsFile;
			}
			if (!class_exists($clsName)) {
				vApi::throwException('110011', 'Auth类定义错误，无法找在auth类文件[' . $clsFile . ']中找到类[' . $clsName . ']');
			}
			$authObj[$authName] = new $clsName;
			if (!method_exists($authObj[$authName], 'auth')) {
				vApi::throwException('110011', 'Auth类定义错误，无法找在auth类文件[' . $clsFile . ']中找到公共方法[auth]');
			}
		}
		vApi::$curAuth = $authName;
		return $authObj[$authName]->auth($authExt);
	}
	/// 获取API ROUTE,通过HTTP方式调用
	
	function getHttpApiRoute() {
		return vApi::_magicVar($_GET[API_ROUTE_VAR_NAME]);
	}
	//-----------------------------------------------------------------
	
	/// 获取API 参数，如果不是POST，将取 $_GET ,通过HTTP方式调用
	
	function getHttpApiParams() {
		//参数选择
		$params = strtoupper($_SERVER['REQUEST_METHOD']) == 'POST' ? $_POST : $_GET;
		if (isset($params[API_ROUTE_VAR_NAME])) {
			unset($params[API_ROUTE_VAR_NAME]);
		}
		if (isset($params['debug'])) {
			unset($params['debug']);
		}
		$params = vApi::_magicVar($params);
		return is_array($params) ? $params : array();
	}
	//-----------------------------------------------------------------
	
	/// 分析API路由
	
	function parseApiRoute($r) {
		$rt = trim($r, " /?&.");
		if (count(explode('..', $r)) > 1 || !preg_match("#^([^\s\.]+)\.([^\s\.]+)\$#sim", $rt, $rm)) {
			vApi::throwException('110001', '无效、非法API路由:[' . $r . ']');
			return FALSE;
		}
		$route = $rm[1];
		$auth = $rm[2];
		$apiFunc = API_FUNC_PREFIX . str_replace('/', '__', $route);
		$apiFile = API_APIS_PATH . '/' . $route . API_FILE_SUFFIX;
		return array(
			'route' => $route, //路由名称
			'auth' => $auth, //授权类型
			'apiFunc' => $apiFunc, //API函数
			'apiFile' => $apiFile
			//apiFile
			
		);
	}
	//------------------------------------------------------------------
	
	/**
	 * vApi::adapter ($name,$type,$is_single=true,$cfgData=false);
	 * 通用的适配器获取方法
	 * @param $name			适配器名称
	 * @param $type			适配器类型
	 * @param $is_single	是否获取单剑
	 * @param $cfgData		适配器初始化参数
	 * @return 相应的适配器实例
	 */
	
	function ADP($name, $type, $is_single = TRUE, $cfgData = FALSE) {
		static $adpObj = array();
		$class = $type . "_" . $name;
		if (isset($adpObj[$class]) && is_object($adpObj[$class]) && $is_single) {
			return $adpObj[$class];
		}
		$cFile = API_ADAPTER_PATH . '/' . $name . '/' . $class . API_EXT_ADAPTER;
		if (!file_exists($cFile)) {
			vApi::throwException('110004', "Can't adapter file [ $cFile ]");
		}
		require_once ($cFile);
		if (!class_exists($class)) {
			vApi::throwException('110005', "class [ $class ]  is not exists in file [ $cFile ] ");
		}
		$c = new $class();
		$iniFunc = ADP_INIT_FUNC;
		if (method_exists($c, $iniFunc)) {
			$c->$iniFunc($cfgData);
		}
		if ($is_single) {
			$adpObj[$class] = $c;
		}
		return $c;
	}
	//-----------------------------------------------------------------
	
	//获取DB实例
	
	function DB() {
		static $oDb = '';
		if (empty($oDb)) {
			$oDb = self::ADP('db', 'mysql', TRUE, vApi::cfg('api_db'));
		}
		return $oDb;
	}
	//-----------------------------------------------------------------
	
	//获取DB实例
	
	function IO() {
		static $stdIo = '';
		if (empty($stdIo)) {
			$stdIo = self::ADP('io', 'file', TRUE, array());
		}
		return $stdIo;
	}
	//-----------------------------------------------------------------
	
	// 获取http对象
	
	function getHttp() {
		static $stHttp = '';
		if (empty($stHttp)) {
			$stHttp = self::ADP('http', 'curl', TRUE, array());
		}
		return $stHttp;
	}
	//-----------------------------------------------------------------
	
	//发送邮件
	
	function sendMail($toList, $subject = '', $body = '') {
		static $mailer = '';
		if (!$mailer) {
			$mailer = self::ADP('mailer', 'smtp', TRUE, vApi::cfg('api_mailer'));
			$error = $mailer->sendEmails($toList, $subject, $body);
			if ($error === TRUE || $error == '') {
				return TRUE;
			}
		}
		return FALSE;
	}
	//-----------------------------------------------------------------
	
	/**
	 * 获取一个基类，实例　默认获取单例
	 * @param mixed $baseName	基类名称（不是类名）
	 * @param mixed $single		是否单例
	 * @return 返回实例
	 */
	
	function getBase($baseName, $single = TRUE) {
		static $baseObj = array();
		if (isset($baseObj[$baseName])) {
			return $baseObj[$baseName];
		}
		$clsName = str_replace("/", "__", $baseName) . API_BASE_CLS_SUFFIX;
		if (!class_exists($clsName)) {
			$clsFile = API_BASE_PATH . '/' . $baseName . API_BASE_FILE_SUFFIX;
			if (!file_exists($clsFile)) {
				vApi::throwException('110004', '无法找到基类文件[' . $clsFile . ']');
			}
			require_once $clsFile;
		}
		if (!class_exists($clsName)) {
			vApi::throwException('110005', '无法找在基类文件[' . $clsFile . ']中找到基类[' . $clsName . ']');
		}
		if ($single) {
			$baseObj[$baseName] = new $clsName;
			return $baseObj[$baseName];
		}
		return new $clsName;
	}
	//-----------------------------------------------------------------
	
	/**
	 * 获取一个Class，实例　默认获取单例
	 * @param mixed $baseName	基类名称（不是类名）
	 * @param mixed $single		是否单例
	 * @return 返回实例
	 */
	
	function getClass($className, $single = TRUE) {
		static $classObj = array();
		if (isset($classObj[$className])) {
			return $classObj[$className];
		}
		$clsName = basename($className) . API_CLASS_SUFFIX;
		if (!class_exists($clsName)) {
			$clsFile = API_CLASS_PATH . '/' . $className . API_CLASS_FILE_SUFFIX;
			if (!file_exists($clsFile)) {
				vApi::throwException('110004', '无法找到Class类文件[' . $clsFile . ']');
			}
			require_once $clsFile;
		}
		if (!class_exists($clsName)) {
			vApi::throwException('110005', '无法在Class类文件[' . $clsFile . ']中找到基类[' . $clsName . ']');
		}
		if ($single) {
			$classObj[$className] = new $clsName;
			return $classObj[$className];
		}
		return new $clsName;
	}
	//-----------------------------------------------------------------
	
	/**
	 * 获取一个参数值
	 *
	 * @param mixed $p	当前接口的所有参数
	 * @param mixed $k	需要获取值的KEY
	 * @param mixed $def 默认值
	 */
	
	function param($p, $k, $def = NULL) {
		return isset($p[$k]) ? $p[$k] : $def;
	}
	//-----------------------------------------------------------------
	
	/**
	 * 内置的静态数据缓存方法，一次执行周期有效
	 * @param mixed $k	缓存 KEY
	 * 注意：命名时，请加上各自的前缀以防冲突如: prefix#keyname
	 * @param mixed $v   缓存的静态数据
	 * @return
	 * 例 ：
	 * 	openApi::kv($k,$v); 	设置一个 KEY 为 $k 值 为 $v 的静态数据
	 * 	openApi::kv($k); 		返回一个 KEY 为 $k 的静态数据
	 * 	openApi::kv();			返回所有静态数据
	 */
	
	function kv($k = NULL, $v = NULL) {
		static $data = array();
		$type = func_num_args();
		switch ($type) {
			case 1:
				return $k && isset($data[$k]) ? $data[$k] : NULL;
			case 2:
				$data[$k] = $v;
				return $v;
			case 0:
				return $data;
		}
		return NULL;
	}
	//-----------------------------------------------------------------
	
	/**
	 * 根据用户服务器环境配置，递归还原变量
	 * @param $mixed
	 * @return 还原后的值
	 */
	
	function _magicVar($mixed) {
		if ((function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) || @ini_get('magic_quotes_sybase')) {
			if (is_array($mixed)) return array_map(array(
				'vApi',
				'_magicVar'
			) , $mixed);
			return stripslashes($mixed);
		}
		return $mixed;
	}
	//-----------------------------------------------------------------
	
	function rst($rst) {
		$isInterCall = self::isInterCall(TRUE, TRUE);
		if (vApi::httpDebug()) {
			header("Content-type:text/html; charset=utf-8");
			echo '<pre>', "RST:\n", print_r($rst, 1) , "<hr>\n";
		}
		if (API_IN_HTTP_REQUEST && !$isInterCall) {
			echo json_encode($rst);
			exit;
		}
		return $rst;
	}
	//是否在HTTP　调试状态
	
	function httpDebug() {
		return API_IS_DEBUG && isset($_GET['debug']) && $_GET['debug'] || (isset($_COOKIE['debug']) && $_COOKIE['debug']);
	}
	//-----------------------------------------------------------------
	
	//事务操作相关
	
	//回滚
	
	function rollback() {
	}
	//-----------------------------------------------------------------
	
	/**
	 * 从一组有序的IDS中获取分页ids，
	 * @param mixed $ids			待切割的IDS数组
	 * @param mixed $page		需要取第几页
	 * @param mixed $count		每页多少个
	 * @param mixed $since_id	最小ID
	 * @param mixed $max_id		最大ID
	 */
	
	function sliceIds($ids, $page, $count, $since_id = NULL, $max_id = NULL) {
		$tmp = array();
		if (empty($ids)) {
			return $tmp;
		}
		$n = 0;
		$rst = array();
		for ($i = 0;$i < count($ids);$i++) {
			$id = $ids[$i];
			if (($max_id == NULL || $id < $max_id) && ($since_id == NULL || $id >= $since_id)) {
				$n++;
				if ($n > $count * ($page - 1)) {
					$rst[] = $id;
					if (count($rst) >= $count) {
						return $rst;
					}
				}
			}
		}
		return $rst;
	}
	//-----------------------------------------------------------------
	
	/**
	 * 策略检查函数
	 * @param mixed $policyStr QUERY格式的策略配置，（可扩展）
	 * 可选策略为
	 * 	method=post,get,delete,put.... 		可选的请求列表,外部HTTP调用时才产生作用
	 *	login=1								是否需要登录用户才能访问
	 * 	search_servers=search_servers		是否需要IP　白名单检查,提供白名单名称
	 * 在API中的调用示例：
	 * 	vApi::policy("method=get,post&login=1&ipCheck=search_servers")
	 */
	
	function policy($policyStr) {
		$policy = vApi::getBase('policy');
		return API_IGNORE_POLICY || $policy->policyChk($policyStr);
	}
	//-----------------------------------------------------------------
	
	/**
	 * API日志 记录方法
	 * 		无参数时，执行物理写操作
	 * 		1个参数时，记录新的一段API日志
	 * 		2个参数时，记录一个新日志信息点
	 * 		0个参数时，写入日志，在 shutdown 时被调用
	 *
	 * @param $n 		日志信息名称
	 * @param $desc 		日志信息描述
	 * @return
	 */
	
	function log($n = NULL, $desc = NULL) {
		static $logStr = '', $logData = array() , $log = '';
		if (!API_ENABLE_LOG) {
			return FALSE;
		}
		//2个参数　记录一个日志信息
		if (func_num_args() == 2) {
			$logData[$n] = json_encode($desc);
		}
		//只有一个参数时，表示某个接口调用开始
		if (func_num_args() == 1 || func_num_args() == 0) {
			$endTime = microtime(TRUE);
			$initLog = empty($logData);
			if (isset($logData['API-CALL'])) {
				//上一次API调用的日志数据
				$logData['API-CALL'] = sprintf("[%s] at [%s], [USER-UID:%s], [AUTH-NAME:%s]", $logData['API-CALL'], $logData['API-TIME'], $logData['USER-UID'], $logData['AUTH-NAME']);
				$logData['API-IPS'] = sprintf("[CIP:%s,	SIP:%s]", $_SERVER['REMOTE_ADDR'], $_SERVER['SERVER_ADDR']);
				$db = vApi::DB();
				$logData['API-SQL-LIST'] = json_encode($db->querys);
				//清空之前的SQL记录
				$db->querys = array();
				$logData['API-LAST-ERROR'] = json_encode(vApi::lastError());
				$logData['API-SPEND-TIME'] = ($endTime - $logData['API-START']) . ' S';
				$logData['API-END'] = 'Called end...';
				unset($logData['API-START']);
				unset($logData['API-TIME']);
				unset($logData['USER-UID']);
				unset($logData['AUTH-NAME']);
				if (API_LOG_IGNORE_RST && !vApi::lastError()) {
					$logData['API-RST'] = '[No api error,RST log is ignore...]';
				}
				$s = '';
				foreach ($logData as $k => $v) {
					$s.= "\n" . str_pad($k, 20) . ":\t" . $v;
				}
				$logStr.= "\n" . $s . "\n" . str_repeat('-', 100);
			}
			if (func_num_args() == 1) {
				$logData = array(
					'API-CALL' => $n,
					'API-TIME' => date("m-d H:i:s") ,
					'API-START' => $endTime,
					'USER-UID' => vApi::uid()
				);
			}
		}
		//无参数调用时　vApi::log();　直接写入日志文件
		if (func_num_args() == 0 && $logStr) {
			$logStr = vApi::utfDecode($logStr);
			if (vApi::httpDebug() || vApi::kv('API-PARAMS-DEBUG')) {
				//ajax request
				if (isset($_GET['_'])) {
					echo '/*', $logStr, '*/';
				}
				else {
					echo "\n\n<pre ><font color=red>\n", str_repeat('-', 100) , "\nDEBUG-LOG", htmlspecialchars($logStr);
				}
			}
			if (API_LOG_IO_TYPE == 'db') {
				return;
				if (!$log) {
					$log = vApi::getBase('log');
				}
				$data = array(
					'log_text' => $logStr,
					'create_time' => date('Y-m-d H:i:s')
				);
				$log->saveApiLog($data);
				unset($data);
				return;
			}
			else {
				$log_file = API_LOG_PATH . '/' . date("Y_m_d") . ".php";
				if (!file_exists($log_file)) {
					$logStr = "<?php exit; ?> \r\n\r\n" . $logStr;
				}
				file_put_contents($log_file, $logStr, FILE_APPEND);
				return;
			}
		}
	}
	//将UNICODE解码
	
	function utfDecode($s) {
		//效率问题，关闭日志UTF转换，TODO 改善性能
		return $s;
		preg_match_all('/\\\\u([[:alnum:]]{4})/', $s, $a);
		foreach ($a[1] as $uniord) {
			$dec = hexdec($uniord);
			$utf = '';
			if ($dec < 128) {
				$utf = chr($dec);
			}
			else if ($dec < 2048) {
				$utf = chr(192 + (($dec - ($dec % 64)) / 64));
				$utf.= chr(128 + ($dec % 64));
			}
			else {
				$utf = chr(224 + (($dec - ($dec % 4096)) / 4096));
				$utf.= chr(128 + ((($dec % 4096) - ($dec % 64)) / 64));
				$utf.= chr(128 + ($dec % 64));
			}
			$s = str_replace("\\u" . $uniord, $utf, $s);
		}
		return urldecode($s);
	}
	//-----------------------------------------------------------------
	
	/**
	 * 获取一个MCQ实例
	 * @param mixed $type 目前只能是 dataReport
	 * @return MCQ 实例
	 */
	
	function getMCQ($type = 'dataReport') {
		static $mcqServers = array();
		if (isset($mcqServers[$type])) {
			return $mcqServers[$type];
		}
		$mcq_serv = vApi::cfg('mcq_cfg', $type);
		if (empty($mcq_serv)) {
			vApi::throwException('110007', '无法找到缓存，或者队列服务器[' . $type . ']');
		}
		//根据缓存配置获取缓存实例
		$mcqServers[$type] = new Memcache; //Memcached
		$servers = explode(' ', trim($mcq_serv['servers']));
		$connect = FALSE;
		foreach ($servers as $server) {
			if (empty($server)) {
				continue;
			}
			$param = explode(':', $server);
			$connect_result = @$mcqServers[$type]->addServer($param[0], $param[1], $mcq_serv['pconnect']);
			$connect = $connect || $connect_result;
			/// 记录mcq连接失败日志
			if (FALSE === $connect) {
				///todo..
				
				//vApi::throwException('110015', '缓存或者MCQ链接失败');
				vApi::log('API-MCQ-C-ERROR', '无法链接MCQ[' . $server . ']');
			}
		}
		return $mcqServers[$type];
	}
	/**
	 * 写入一个数据到 MCQ
	 * @param mixed $mcqName		队列名称目前只能是 log sync
	 * @param string $qName		队列名称 数据上报时，队列名 用常量 API_REP_QNAME
	 * @param mixed $data		写入队列的数据
	 */
	
	function wMCQ($mcqName, $qName, $data) {
		$mcqObj = vApi::getMCQ($mcqName);
		$data = json_encode($data);
		$tmp = $mcqObj->set($qName, $data, 0, 0);
		if (!$tmp) {
			//写入MCQ失败
			
			//vApi::throwException('110014', '缓存或者MCQ写入失败');
			vApi::log('API-MCQ-W-ERROR', '无法写入MCQ[' . $mcqName . ':' . $qName . ']');
		}
		return $tmp;
	}
	/**
	 * 从队列中读取一个数据
	 * @param string $mcqName	队列名称目前只能是 dataReport
	 * @param string $qName		队列名称 数据上报时，队列名 用常量 API_REP_QNAME
	 */
	
	function rMCQ($mcqName, $qName) {
		$mcqObj = vApi::getMCQ($mcqName);
		return $mcqObj->get($qName);
	}
	/**
	 * 上报、同步一个数据项
	 */
	
	function report($repData = array() , $isHttp = FALSE) {
	}
	/**
	 * 脚本关闭时的操作，关闭链接，写日志等操作将在此
	 */
	
	function shutDown() {
		//todo...
		try {
			vApi::log();
			vApi::closeServiceConnecttion();
		}
		catch(Exception $ex) {
			trigger_error($ex->getMessage() . '(' . $ex->getCode() . ')' . $ex, E_USER_ERROR);
		}
	}
	/**
	 * 关闭一些 底层服务链接 ，如 DB MC
	 */
	
	function closeServiceConnecttion() {
		//关闭DB链接
		vApi::DB()->close();
		//todo.. 关闭MC链接
		
	}
	//-----------------------------------------------------------------
	
	/**
	 * 抛出错误
	 * @param mixed	$errno		错误码
	 * @param mixed	$err		错误信息
	 * @param string	$debugErr	管理员看的高级信息，可能会暴露更多的信息
	 */
	
	function throwException($errno, $err, $debugErr = NULL) {
		$api = vApi::kv('API-LAST-CALL');
		$errMsg = (API_IS_DEBUG && $debugErr) ? $debugErr : $err;
		$errObj = array(
			'api' => $api,
			'errno' => $errno,
			'err' => $errMsg
		);
		vApi::kv('error#last_err', $errObj);
		throw new vApiException($errObj);
	}
	/// 最后一个错误信息
	
	function lastError() {
		return vApi::kv('error#last_err');
	}
	//-----------------------------------------------------------------
	
	/// 返回当前来源
	
	function source($source = '') {
		static $stSource = '';
		if (func_num_args()) {
			$stSource = $source;
			VF::checkSource($stSource);
		}
		if (empty($stSource)) {
			$stSource = self::param($_REQUEST, 'source');
			VF::checkSource($stSource);
		}
		return $stSource;
	}
	/// 返回当前请求的数据格式
	
	function returnFormat($format = '') {
		static $stReturnFormat = '';
		if (func_num_args()) {
			$stReturnFormat = $format;
		}
		if (empty($stReturnFormat)) { // 从request获取
			$stReturnFormat = self::param($_REQUEST, '_format');
		}
		return $stReturnFormat;
	}
	/// 用堆栈的方法实现API调用API标识
	
	function isInterCall($isInterCall = NULL, $end = NULL) {
		static $stInterCallQueue = array();
		switch (func_num_args()) {
			case 1:
				return array_push($stInterCallQueue, $isInterCall);
			case 2:
				return array_pop($stInterCallQueue);
			default:
				return end($stInterCallQueue);
		}
	}
	/// 获取请求IP
	
	function ip($ip = '') {
		static $stIp = '';
		if (func_num_args()) {
			$stIp = VF::ip($ip);
		}
		if (empty($stIp)) { // 从request获取
			$stIp = VF::ip(self::param($_REQUEST, '_ip'));
		}
		return $stIp;
	}
	/// 获取token
	
	function token() {
		$token = isset($_COOKIE[TOKEN_PASS_KEY]) ? $_COOKIE[TOKEN_PASS_KEY] : '';
		self::log('Token In Cookie', $token);
		return $token;
	}
}
//---------------------------------------------------------------------

/// 用于处理　API　内部错误的　Exception 类

class vApiException extends Exception {
	var $err = array();
	
	function vApiException($eArr = array()) {
		$this->err = $eArr;
	}
	//提供给外界调用的错误信息获取接口
	
	function apiErr() {
		if (API_IN_HTTP_REQUEST) {
			$rst['err'] = $this->err;
			echo vApi::rst($rst);
			exit;
		}
		return $this->err;
	}
	//获取错误码
	
	function errno() {
		return isset($this->err['errno']) ? $this->err['errno'] : 0;
	}
	
	function __toString() {
		return sprintf('api error(%s)(%s):%s', $this->err['api'], $this->err['errno'], $this->err['err']);
	}
}
//---------------------------------------------------------------------

/// 兼容原主站的函数 在 DB 类中存在 物理迁移后可以删除
if (API_IN_HTTP_REQUEST && !function_exists('LOGSTR')) {
	
	function LOGSTR() {
	}
	
	function F() {
	}
	define('LOG_LEVEL', 2);
	define('LOG_LEVEL_ERROR', 'error');
	define('LOG_LEVEL_WARNING', 'warning');
	define('LOG_LEVEL_INFO', 'info');
	/// xwb 日志, DB、IO、MC、API长操作时间, 以秒为单位
	define('LOG_DB_WARNING_TIME', 0.5);
	define('LOG_IO_WARNING_TIME', 0.5);
	define('LOG_MC_WARNING_TIME', 0.5);
	define('LOG_API_WARNING_TIME', 1);
}

function XDLOG($str) {
	if (defined('API_IS_DEBUG') && API_IS_DEBUG) {
		$msg = is_array($str) ? print_r($str, TRUE) : $str;
		$msg = "\r\n" . $msg . "\r\n" . date('Y-m-d H:i:s') . "\r\n------------------------------\r\n";
		$log_file = API_ROOT_PATH . '/log/' . date('Y-m-d') . '.xdebug.php';
		vApi::ADP('io', 'file')->write($log_file, $msg, TRUE);
	}
}
//---------------------------------------------------------------------

///　所有　base 类的基类

class apiBase {
	
	function __construct() {
	}
	/**
	 * 缓存调用，当缓存不存在时重建
	 * @param string $funcName 当前类的　非缓存数据获取方法
	 * @param array  提供给　$funcName　的参数，数组的顺序即为　$funcName　的参数顺序
	 * @param mixed $cacheOpt	缓存选项 两种组合如下
	 * 				$cacheOpt['kname']　+　$cacheOpt['kvar'] 内置的命名CACHE（见缓存列表）
	 * 				$cacheOpt['k'] 自定义的缓存KEY　　$cacheOpt['t']	缓存时间　将使用　custom　指定的服务集
	 * 				$cacheOpt['isList'] 不提供，则以配置中的　n　为准，提供，则参数优先 (自定义缓存无效)
	 * @param mixed $reset　是否重置缓存
	 * @return 具体的数据
	 */
	
	function cacheCall($funcName, $params, $cacheOpt, $reset = FALSE) {
		$data = FALSE;
		$apiCache = vApi::getBase('apiCache');
		$ccfg = $apiCache->cfg($cacheOpt['kname']);
		$reset = API_CACHE_FORCE_RESET ? TRUE : $reset;
		// 随机重建机制 避免热数据 自动失效带来的并发雪蹦问题
		if (empty($reset) && isset($cacheOpt['kname'])) {
			if (isset($ccfg['hit'])) {
				list($hit, $max) = explode('/', $ccfg['hit']);
				$reset = (rand(1, $max) == $hit);
			}
		}
		if ($reset) {
			vApi::log("API-CACHE-IGNORE", 'Force reset one cache .');
		}
		if (!$reset) {
			if (isset($cacheOpt['kname']) && isset($cacheOpt['kvar'])) {
				// 是否LIST缓存，参数优先
				$isList = (isset($cacheOpt['isList']) && $cacheOpt['isList']) || $ccfg['n'];
				if ($isList) {
					$data = $apiCache->listGet($cacheOpt['kname'], $cacheOpt['kvar']);
				}
				else {
					$data = $apiCache->get($cacheOpt['kname'], $cacheOpt['kvar']);
				}
			}
		}
		//需要重建，或者缓存获取失败
		if ($reset || $data == FALSE) {
			$data = call_user_func_array(array(
				$this,
				$funcName
			) , $params);
			if (isset($cacheOpt['kname']) && isset($cacheOpt['kvar'])) {
				//　是否LIST缓存，参数优先
				$ttl = isset($cacheOpt['t']) ? $cacheOpt['t'] : $ccfg['t'];
				$isList = (isset($cacheOpt['isList']) && $cacheOpt['isList']) || $ccfg['n'];
				if ($isList) {
					$setSt = $apiCache->listSet($cacheOpt['kname'], $cacheOpt['kvar'], $data, $ttl);
				}
				else {
					$setSt = $apiCache->set($cacheOpt['kname'], $cacheOpt['kvar'], $data, $ttl);
				}
			}
		}
		return $data;
	}
	/**
	 * 批量性的缓存调用
	 * @param string $funcName 当前类的　非缓存数据获取方法 此方法返回的结果应该是用　$kvar 为索引的数组
	 * @param array  提供给　$funcName　的参数，数组的顺序即为　$funcName　的参数顺序
	 * 							第一个参数即　$params[0]  必须是　类 kvars 型
	 * @param mixed $cacheOpt	缓存选项 只适用于内置命名缓存
	 * 				$cacheOpt['kname']　+　$cacheOpt['kvars'] 内置的命名CACHE（见缓存列表）
	 * 				$cacheOpt['t']	缓存时间,未指定时，将使用　缓存配置中的选项
	 * 				$cacheOpt['isList'] 不提供，则以配置中的　n　为准，提供，则参数优先 (自定义缓存无效)
	 * @params array 传递给　$funcName　的参数，
	 * @param mixed $reset　是否重置缓存
	 * @return 具体的数据
	 */
	
	function mutiCacheCall($funcName, $params, $cacheOpt, $reset = FALSE) {
		if ($reset) {
			vApi::log("API-MCACHE-IGNORE", 'Force reset muti cache.');
		}
		$apiCache = vApi::getBase('apiCache');
		$ccfg = $apiCache->cfg($cacheOpt['kname']); // 命名缓存配置
		$ttl = isset($cacheOpt['t']) ? $cacheOpt['t'] : $ccfg['t'];
		$isList = (isset($cacheOpt['isList']) && $cacheOpt['isList']) || $ccfg['n'];
		//需要物理查询的部分
		$query_kvars = $cacheOpt['kvars'];
		$data = array();
		//非重建　尝试从缓存中获取数据
		if (!$reset) {
			$mcData = $apiCache->getMuti($cacheOpt['kname'], $cacheOpt['kvars'], $isList);
			if (is_array($mcData)) {
				$query_kvars = is_array($mcData['nohit_kvars']) ? $mcData['nohit_kvars'] : array();
				$data = $mcData['values'];
				unset($mcData);
			}
		}
		//有一部分数据在缓存中找不到
		if (!empty($query_kvars)) {
			$params[0] = $query_kvars;
			$dbData = call_user_func_array(array(
				$this,
				$funcName
			) , $params);
			if (!is_array($dbData)) {
				vApi::throwException('', '使用　mutiCacheCall　时　[' . $funcName . '] 返回值有误！' . json_encode($dbData) . '');
			}
			foreach ($dbData as $kvar => $value) {
				if ($isList) {
					$apiCache->listSet($cacheOpt['kname'], $kvar, $value);
				}
				else {
					$apiCache->set($cacheOpt['kname'], $kvar, $value);
				}
				$data[$kvar] = $value;
			}
		}
		//11-15 增加 还原原来的顺序
		$rstData = array();
		foreach ($cacheOpt['kvars'] as $kid) {
			$kid = "" . $kid;
			isset($data[$kid]) && $rstData[$kid] = $data[$kid];
		}
		unset($data);
		return $rstData;
	}
	/**
	 * 将一个数据库查询出来的结果，转换成　关键 key->row 的结构
	 * @param mixed $data		原始数据
	 * @param mixed $fieldName	需要取出来做索引key的字段名
	 */
	
	function formatKeyRows($data, $fieldName) {
		$rstData = array();
		if (!is_array($data)) {
			vApi::throwException('110013', '系统繁忙', '数据格式错误');
		}
		if (isset($data['errno']) && $data['errno']) {
			vApi::throwException($data['errno'], $data);
		}
		foreach ($data as $r) {
			$rstData[$r[$fieldName] . ''] = $this->_buildData($r);
		}
		return $rstData;
	}
	// 处理数据
	
	function _buildData($rs) {
		if (empty($rs)) {
			return $rs;
		}
		// 去掉IP 和  app_source
		if (isset($rs['ip'])) {
			unset($rs['ip']);
		}
		if (isset($rs['app_source'])) {
			unset($rs['app_source']);
		}
		return $rs;
	}
	// 返回IP app_source信息
	
	function _defData() {
		return array(
			'ip' => vApi::ip() ,
			'app_source' => vApi::source()
		);
	}
}
//---------------------------------------------------------------------

//全局方法类

class VF {
	/**
	 * Render File
	 * @param fileName $_viewFile_
	 * @param data $_data_
	 * @param $_return_
	 */
	public 
	function render($_viewFile_, $_data_ = NULL, $_return_ = FALSE) {
		$_viewFile_ = API_TPL_PATH . "/{$_viewFile_}.tpl.php";
		if (is_array($_data_)) extract($_data_);
		else $data = $_data_;
		if ($_return_) {
			ob_start();
			ob_implicit_flush(FALSE);
			require ($_viewFile_);
			return ob_get_clean();
		}
		else require ($_viewFile_);
	}
	// 请求参数签名
	
	function signRequest($params, $time = '') {
		$params[API_REQUEST_TIME_KEY] = $time ? $time : APP_LOCAL_TIMESTAMP;
		ksort($params);
		$req = urldecode(http_build_query($params));
		$secret = VF::getSourceSecret();
		$params[API_REQUEST_SIGN_KEY] = md5($req . "&$secret");
		return http_build_query($params);
	}
	// 检查请求参数
	
	function checkRequestSign() {
		$params = strtolower($_SERVER['REQUEST_METHOD']) === 'post' ? $_POST : $_GET;
		if (empty($params[API_REQUEST_SIGN_KEY])) {
			vApi::throwException('210020', '参数签名为空');
		}
		if (empty($params[API_REQUEST_TIME_KEY])) {
			vApi::throwException('210021', '时间戳参数为空');
		}
		$sign = $params[API_REQUEST_SIGN_KEY];
		unset($params[API_REQUEST_SIGN_KEY]);
		unset($params[API_ROUTE_VAR_NAME]);
		// 新浪验证特殊处理
		if (isset($params['oauth_token'])) {
			unset($params['oauth_token']);
		}
		if (isset($params['oauth_verifier'])) {
			unset($params['oauth_verifier']);
		}
		//QQ验证特殊处理
		if (isset($params['openid'])) {
			unset($params['openid']);
		}
		if (isset($params['openkey'])) {
			unset($params['openkey']);
		}
		// 人人验证处理
		if (isset($params['code'])) {
			unset($params['code']);
		}
		if (isset($params['error'])) {
			unset($params['error']);
		}
		if (isset($params['error_description'])) {
			unset($params['error_description']);
		}
		ksort($params);
		$req = urldecode(http_build_query($params));
		$secret = VF::getSourceSecret();
		$chkSign = md5($req . "&$secret");
		if ($sign != $chkSign) {
			vApi::log('Request Sign', "pass=$sign;chk=$chkSign;md5str=$req&$secret");
			vApi::throwException('210020', '无效参数签名');
		}
		return TRUE;
	}
	// 获取source 对应的secret签名
	
	function getSourceSecret() {
		$source = vApi::source();
		$key = self::checkSource($source);
		global $G_API_CONFIG;
		return $G_API_CONFIG['source'][$key]['secret'];
	}
	/**
	 * 创建Token
	 * @param $uid
	 */
	
	function getToken($uid) {
		$uid && $info = vApi::getBase('account')->getAccount($uid);
		if (empty($info)) {
			return '';
		}
		$source = vApi::source();
		$token = vApi::vMd5($uid, $source);
		$data = array(
			'uid' => $uid,
			'app_source' => $source,
			'token' => $token,
			'chk_token' => vApi::vMd5($token, $info['password']) ,
			'create_time' => APP_LOCAL_TIMESTAMP
		);
		$token = vApi::getBase('user_token')->save($uid, $data);
		vApi::log('Token String', $token);
		if ($token) {
			self::firstLoginAct($uid);
		}
		return $token;
	}
	/**
	 * 第一次登陆的操作
	 * @param $uid
	 */
	
	function firstLoginAct($uid) {
		static $loginLock = ''; // 一个进程只进入一次登录
		if ($loginLock) {
			return TRUE;
		}
		$loginLock = TRUE;
		$key = date('Y-m-d', APP_LOCAL_TIMESTAMP) . "#$uid";
		$oCache = vAPi::getBase('apiCache');
		if (!$oCache->add('firstLogin', $key, $uid)) {
			return TRUE;
		}
	}
	/**
	 * 解析Token
	 * @param $token
	 */
	
	function parseToken($token) {
		// 解析Token
		$token && $info = vApi::getBase('user_token')->getByToken($token);
		if (empty($info['uid'])) {
			vApi::throwException('210010', 'token数据错误', 'not find the token row');
		}
		$uinfo = vApi::getBase('account')->getAccount($info['uid']);
		if (empty($uinfo['uid']) || $uinfo['deleted']) {
			vApi::throwException('210013', '无效用户', 'not find the token user');
		}
		// 验证token有效
		if (vApi::vMd5($token, $uinfo['password']) != $info['chk_token']) {
			vApi::throwException('210012', '用户密码已修改，请重新登录');
		}
		$sourceCfg = vApi::cfg('source', $info['app_source']);
		if ($sourceCfg['exp'] && APP_LOCAL_TIMESTAMP - $info['create_time'] > $sourceCfg['exp']) {
			vApi::throwException('210011', 'token过期');
		}
		return array(
			'uid' => $uinfo['uid'],
			'source' => $sourceCfg['key']
		);
	}
	
	function checkSource($source) {
		if (empty($source)) {
			vApi::throwException('210012', '无效source');
		}
		$cfg = vApi::cfg('source');
		if (empty($cfg[$source])) {
			vApi::throwException('210012', '无效source');
		}
		return $source;
	}
	//------------------------------------------------------------------
	
	/**
	 * APP::redirect($mRoute,$type=1);
	 * 重定向 并退出程序
	 * @param $url
	 * @param $type 	1:给定URL, 2:给定URL,用JS跳
	 * @return 无返回值
	 */
	
	function redirect($url, $type = 1) {
		switch ($type) {
			case 2:
				echo '<script>window.location.href="' . addslashes($url) . '";</script>';
			break;
			default:
				header("Location: " . $url);
			break;
		}
		exit;
	}
	///　IP客户IP
	
	function ip($ip = '') {
		if (empty($ip)) {
			//如果需要指定IP列表，则随机从列表中取一个返回
			if (defined('CLIENT_IP_TEMP_LIST') && CLIENT_IP_TEMP_LIST) {
				$ips = explode(',', CLIENT_IP_TEMP_LIST);
				return $ips[array_rand($ips) ];
			}
			if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR") , "unknown")) $ip = getenv("REMOTE_ADDR");
			else if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP") , "unknown")) $ip = getenv("HTTP_CLIENT_IP");
			else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR") , "unknown")) $ip = getenv("HTTP_X_FORWARDED_FOR");
			else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) $ip = $_SERVER['REMOTE_ADDR'];
			else $ip = "unknown";
		}
		return preg_match('/^((25[0-5]|2[0-4]\d|(1\d|[1-9])?\d)\.){3}(25[0-5]|2[0-4]\d|(1\d|[1-9])?\d)$/', $ip) ? $ip : '0.0.0.0';
	}
	// html转义输出
	
	function O($str, $return = FALSE) {
		if ($return) {
			return htmlspecialchars($str, ENT_COMPAT);
		}
		echo htmlspecialchars($str, ENT_COMPAT);
	}
	/**
	 * 获取一段文本
	 * @param mixed $str			原始文本
	 * @param mixed $length		需要截取的长度
	 * @param mixed $start		开始位置，默认为　0
	 * @param mixed $suffix		截取后是否需要增加后缀如　...　默认为空
	 * @param mixed $charset		文本的字符集，默认为 utf-8
	 * @return string
	 */
	
	function mSubstr($str, $length, $start = 0, $suffix = "", $charset = "utf-8") {
		if (function_exists("mb_substr")) $return = mb_substr($str, $start, $length, $charset);
		elseif (function_exists('iconv_substr')) {
			$return = iconv_substr($str, $start, $length, $charset);
		}
		else {
			$re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
			$re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
			$re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
			$re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
			preg_match_all($re[$charset], $str, $match);
			$slice = join("", array_slice($match[0], $start, $length));
			$return = $slice;
		}
		if ($start == 0 && strlen($str) == strlen($return)) {
			return $return;
		}
		return $return . $suffix;
	}
}
//---------------------------------------------------------------------

//建筑物基类

class Building extends apiBase {
	var $db;
	var $table;
	var $bcfg;
	static $routeTypeMap = array(
		'building/producte' => 'p',
		'building/decoration' => 'dr',
		'building/defense' => 'df'
	);
	
	function __construct() {
		$this->db = vApi::DB();
		$this->table = $this->db->getTable(API_TB_BUILDING);
	}
	// 获取单例
	
	function getOne() {
		$stObj = '';
		if (empty($stObj)) {
			$stObj = new Building();
		}
		return $stObj;
	}
	/**
	 * 从DB批量获取内容
	 * @param $ids
	 */
	
	function batchGet_db($ids) {
		if (empty($ids)) {
			return array();
		}
		$db = vApi::DB();
		$inStr = $db->buildIn($ids);
		$sql = $db->SQL("SELECT * FROM `%s` WHERE `id` IN ($inStr)", $this->table);
		$rs = $db->query($sql);
		if (is_array($rs)) {
			$rs = $this->formatKeyRows($rs, 'id');
		}
		return $rs;
	}
	// 批量获取内容
	
	function batchGet($ids) {
		if (empty($ids)) {
			return array();
		}
		$rs = $this->mutiCacheCall('batchGet_db', array(
			''
		) , array(
			'kname' => 'building',
			'kvars' => $ids
		));
		return $rs;
	}
	/**
	 * 从DB获取内容
	 * @param $id
	 */
	
	function get_db($id) {
		if (empty($id)) {
			return array();
		}
		$db = vApi::DB();
		$sql = $db->SQL("SELECT * FROM `%s` WHERE `id`='%s'", $this->table, $id);
		$rs = $db->getRow($sql);
		return $this->_buildData($rs);
	}
	// 获取内容
	
	function get($id) {
		if (empty($id)) {
			return array();
		}
		$rs = $this->cacheCall('get_db', array(
			$id
		) , array(
			'kname' => 'building',
			'kvar' => $id
		));
		return $rs;
	}
	// 根据route获取ID
	
	function getIdByRoute_db($route) {
		if (empty($route)) {
			return array();
		}
		$db = vApi::DB();
		$sql = $db->SQL("SELECT id FROM `%s` WHERE `route`='%s'", $this->table, $route);
		$rs = $db->query($sql);
		$data = array();
		if (is_array($rs)) {
			foreach ($rs as $row) {
				$data[] = $row['id'];
			}
		}
		return $data;
	}
	// 获取内容
	
	function getIdByRoute($route) {
		if (empty($route)) {
			return array();
		}
		$rs = $this->cacheCall('getIdByRoute_db', array(
			$route
		) , array(
			'kname' => 'building',
			'kvar' => "route=$route"
		));
		return $rs;
	}
	/**
	 * 新增操作
	 * @param array $data
	 */
	
	function save($id, $data) {
		if (empty($data)) {
			return FALSE;
		}
		$rst = $this->db->save($data, $id, API_TB_BUILDING);
		if ($rst) {
			$this->cacheCall('get_db', array(
				$id
			) , array(
				'kname' => 'building',
				'kvar' => $id
			) , TRUE);
		}
		return $rst;
	}
	/**
	 * 删除操作
	 * @param bigint $ids
	 */
	
	function del($ids) {
		if (empty($ids)) {
			return FALSE;
		}
		$ids = is_array($ids) ? $ids : explode(',', $ids);
		$inStr = $this->db->buildIn($ids);
		$rst = $this->db->execute("Delete From {$this->table} Where id In ($inStr)");
		if ($rst) {
			$this->mutiCacheCall('batchGet_db', array(
				''
			) , array(
				'kname' => 'building',
				'kvars' => $ids
			) , TRUE);
		}
		return $rst;
	}
	// 设置建筑配置
	
	function setBCfg($bcfg) {
		$this->bcfg = $bcfg;
		return $this;
	}
	// 创建建筑物，子类重构
	
	function payRes_build($uid, $level = 1) {
		if (empty($this->bcfg['add_cfg'])) {
			vApi::throwException('417301', '非法建筑');
		}
		$cfg = json_decode($this->bcfg['add_cfg'], TRUE);
		if (empty($cfg[$level])) {
			vApi::throwException('417302', '不符合条件');
		}
		$cfg = $cfg[$level];
		$oRes = vApi::getBase('userRes');
		if (isset($cfg['res_chk']) && TRUE !== $oRes->chk($uid, $cfg['res_chk'])) {
			vApi::throwException('417302', '不符合条件');
		}
		if (isset($cfg['ext_chk'])) {
			$this->chkExt($uid, $cfg['ext_chk']);
		}
		if (!$oRes->pay($uid, $cfg['res_pay'])) {
			vApi::throwException('417302', '不符合条件');
		}
		return TRUE;
	}
	// 升级建筑物，子类重构
	
	function payRes_upgrade($uid, $level) {
		if (empty($this->bcfg['update_cfg'])) {
			vApi::throwException('417301', '非法建筑');
		}
		$cfg = json_decode($this->bcfg['update_cfg'], TRUE);
		if (empty($cfg[$level])) {
			vApi::throwException('417302', '不符合条件');
		}
		$cfg = $cfg[$level];
		$oRes = vApi::getBase('userRes');
		if (isset($cfg['res_chk']) && TRUE !== $oRes->chk($uid, $cfg['res_chk'])) {
			vApi::throwException('417302', '不符合条件');
		}
		if (isset($cfg['ext_chk'])) {
			$this->chkExt($uid, $cfg['ext_chk']);
		}
		if (!$oRes->pay($uid, $cfg['res_pay'])) {
			vApi::throwException('417302', '不符合条件');
		}
		return TRUE;
	}
	// 回收建筑物，子类重构
	
	function payRes_sale($uid, $level) {
		if (empty($this->bcfg['del_cfg'])) {
			vApi::throwException('417301', '非法建筑');
		}
		$cfg = json_decode($this->bcfg['del_cfg'], TRUE);
		if (empty($cfg[$level])) {
			vApi::throwException('417302', '不符合条件');
		}
		$cfg = $cfg[$level];
		$oRes = vApi::getBase('userRes');
		if (!$oRes->add($uid, $cfg['res_add'])) {
			vApi::throwException('417302', '不符合条件');
		}
		return TRUE;
	}
	// 检查其它参数
	
	function chkExt($uid, $cfg) {
		if (isset($cfg['m']) || isset($cfg['b'])) {
			$blist = vApi::getBase('user_building')->getBuildByUid($uid);
			if (empty($blist['m']) || empty($blist['b'])) {
				vApi::throwException('417303', 'User system building Error');
			}
			// 主城等级
			if (isset($cfg['m']) && $blist['m'] < $cfg['m']) {
				vApi::throwException('417302', '不符合条件：主城等级不够');
			}
			// 兵营等级
			if (isset($cfg['b']) && $blist['b'] < $cfg['b']) {
				vApi::throwException('417302', '不符合条件：兵营等级不够');
			}
		}
		return TRUE;
	}
	// 创建建筑时格外操作
	
	function afterCreate($id, $data) {
		return TRUE;
	}
	// 删除建筑时格外操作
	
	function afterDelete($id) {
		return TRUE;
	}
	// 升级建筑时格外操作
	
	function afterUpdate($oid) {
		return TRUE;
	}
}
